package source;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

public class DoublePointer {

    /**
     * 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后，短语正着读和反着读都一样。
     * 则可以认为该短语是一个 回文串 。
     * <p>
     * 字母和数字都属于字母数字字符。
     *
     * @param s 原始字符串
     * @return 如果它是 回文串 ，返回 true ；否则，返回 false 。
     */
    public boolean isPalindrome(String s) {

        boolean isPalindrome = true;
        String str = s.toLowerCase();
        int j;
        int i = 0;
        j = str.length() - 1;
        while (i < j) {
            if ((str.charAt(i) > 'z' || str.charAt(i) < 'a') && (str.charAt(i) > '9' || str.charAt(i) < '0')) {
                i++;
                continue;
            }
            if ((str.charAt(j) > 'z' || str.charAt(j) < 'a') && (str.charAt(j) > '9' || str.charAt(j) < '0')) {
                j--;
                continue;
            }
            if (str.charAt(i) != str.charAt(j)) {
                isPalindrome = false;
                break;
            }
            i++;
            j--;
        }
        return isPalindrome;
    }

    /**
     * 给定字符串 s 和 t ，判断 s 是否为 t 的子序列。
     * <p>
     * 字符串的一个子序列是原始字符串删除一些（也可以不删除）字符而不改变剩余字符相对位置形成的新字符串。
     * （例如，"ace"是"abcde"的一个子序列，而"aec"不是）。
     *
     * @param s 子序列
     * @param t 主字符串
     * @return s 是否为子序列
     */
    public boolean isSubsequence(String s, String t) {
        int i = 0;
        int j = 0;
        while (i < t.length() && j < s.length()) {
            if (t.charAt(i) == s.charAt(j)) {
                j++;
            }
            i++;
        }
        return j == s.length();
    }

    /**
     * 两数之和 II -输入有序数组
     * <p>
     * 给你一个下标从 1 开始的整数数组 numbers ，该数组已按 非递减顺序排列  ，请你从数组中找出满足相加之和等于目标数 target 的两个数。
     * 如果设这两个数分别是 numbers[index1] 和 numbers[index2] ，则 1 <= index1 < index2 <= numbers.length 。
     * <p>
     * 以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
     * <p>
     * 你可以假设每个输入 只对应唯一的答案 ，而且你 不可以 重复使用相同的元素。
     * <p>
     * 你所设计的解决方案必须只使用常量级的额外空间。
     *
     * @param numbers 有序数组
     * @param target  两数之和
     * @return 返回这两个整数的下标 index1 和 index2
     */
    public int[] twoSum(int[] numbers, int target) {
        int i = 0, j = numbers.length - 1;
        while (i < j) {
            if (numbers[i] + numbers[j] == target)
                return new int[]{i + 1, j + 1};
            else if (numbers[i] + numbers[j] > target) {
                j--;
            } else i++;
        }
        return null;
    }

    /**
     * 给定一个长度为 n 的整数数组 height 。
     * 有 n 条垂线，第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
     * <p>
     * 找出其中的两条线，使得它们与 x 轴共同构成的容器可以容纳最多的水。
     * <p>
     * 返回容器可以储存的最大水量。
     *
     * @param height 高度数组
     * @return 最大容量
     */
    public int maxArea(int[] height) {
        int max = 0;
        int i = 0, j = height.length - 1;
        while (i < j) {
            if (height[i] >= height[j]) {
                max = Math.max((j - i) * height[j], max);
                j--;
            } else if (height[i] < height[j]) {
                max = Math.max((j - i) * height[i], max);
                i++;
            }
        }
        return max;
    }

    /**
     * 三数之和
     * <p>
     * 判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ，
     * 同时还满足 nums[i] + nums[j] + nums[k] == 0 。
     * 请你返回所有和为 0 且不重复的三元组。
     * <p>
     * 注意：答案中不可以包含重复的三元组。
     *
     * @param nums 给你一个整数数组 nums ，
     * @return 返回所有和为 0 且不重复的三元组。
     */
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        HashSet<List<Integer>> result = new HashSet<>();
        int length = nums.length;
        int ij;
        int lastK, lastJ = length - 1;

        for (int i = 0; i < length - 2; i++) {
            lastK = length - 1;

            if (nums[i] > 0)
                break;
            for (int j = i + 1; j <= lastJ; j++) {
                ij = nums[i] + nums[j];
                if (ij > 0) {
                    lastJ = j;
                    break;
                }
                for (int k = lastK; k > j; k--) {
                    if (ij + nums[k] == 0) {
                        List<Integer> list = new ArrayList<>();
                        list.add(nums[i]);
                        list.add(nums[j]);
                        list.add(nums[k]);
                        result.add(list);
                        break;
                    }
                    if (ij + nums[k] < 0) {
                        lastK = k;
                        break;
                    }
                }
            }
        }

        return new ArrayList<>(result);
    }
}
